#include "sample/sample_set.h"

#include "matrices/matrix.h"
#include "comm/log.h"

#include <json/json.h>

#include <sstream>

using namespace nymphaea;
using namespace nymphaea::sample;

SampleSet::SampleSet():
	mpFeature(NULL),
	mpTarget(NULL),
	mpWeight(NULL),
	mFeatureDim(0),
	mTargetDim(0),
	mSampleNumber(0)
{
}

SampleSet::~SampleSet()
{
	delete mpFeature;
	delete mpWeight;
	delete mpTarget;
}

void SampleSet::init_matrix()
{
	// reserve idx:0 for intercept
	mpFeature = new matrices::Matrix(mSampleNumber, mFeatureDim + 1);
	mpWeight = new matrices::Matrix(mSampleNumber, 1);
	mpTarget = new matrices::Matrix(mSampleNumber, 1);
	//initial weight of sample
	mpWeight->set_all(1.0F);
}

int SampleSet::load_from_bytes(const std::string& data)
{

	std::istringstream ss(data);
	std::string line;

	size_t current_sample_idx = 0;
	bool is_first_line = true;

	while(getline(ss, line, '\n')){
		//for first line, load header
		if(is_first_line){
			if(!load_header_from_bytes(line)){
				LOG("load sample header failed");
				return -1;
			}
			init_matrix();
			is_first_line = false;
		}else{
			if(load_sample_from_bytes(line, current_sample_idx)){
				LOG("load sample features failed");
				return -1;
			}
			++current_sample_idx;
		}
	}

	if(current_sample_idx+1 != mSampleNumber){
		return -1;
	}

	return 0;
}

int SampleSet::load_header_from_bytes(const std::string& data)
{
	Json::Reader reader;
	Json::Value header;

	if(!reader.parse(data, header)){
		LOG("Json parsing failed");
		// error log
		return -1;
	}
	mSampleNumber = header.get("sample_number", 0).asUInt();
	mTargetDim = header.get("target_dim", 0).asUInt();
	mFeatureDim = header.get("feature_dim", 0).asUInt();

	if(0 == mSampleNumber || 0 == mTargetDim || 0 == mFeatureDim){
		LOG("Invalid sample set header");
		return -1;
	}
	return 0;
}


int SampleSet::load_sample_from_bytes(const std::string& data, size_t sample_idx)
{
	Json::Reader reader;
	Json::Value sample;

	if(!reader.parse(data, sample)){
		LOG("Json parsing failed");
		// error log
		return -1;
	}

	if(2 != sample.size()){
		LOG("Invalid sample column");
		return -1;
	}

	int target_idx = 0;
	size_t target = sample[target_idx].asUInt();

	if(! (target < mTargetDim)){
		LOG("Invalid target label");
		return -1;
	}

	int feature_idx = 1;
	for(size_t idx = 0; idx < (sample[feature_idx]).size(); ++idx){
		Json::Value feature = (sample[1])[idx];
		if(2 != feature.size()){
			LOG("Corrupted feature storage");
			return -1;
		}
		int fid_idx = 0;
		int fval_idx = 1;
		size_t f_id = feature[fid_idx].asUInt();
		double f_val = feature[fval_idx].asDouble();

		if(! (f_id < mFeatureDim)){
			LOG("Overflowed feature id");
			return -1;
		}
		//idx:0 is reserved for intercept
		mpFeature->set_element_by_idx(sample_idx, f_id+1, f_val);
	}

	//idx:0 is for intercept, always set to one 
	mpFeature->set_element_by_idx(sample_idx, 0, 1.0F); 

	mpTarget->set_element_by_idx(sample_idx, 0, target);

	return 0;
}
